/*
 * To change this template, choose Tools | Templates
 * and open the template in the editor.
 */
package cn.ekuma.data.dao;

import cn.ekuma.data.dao.bean.CompositeKey;
import cn.ekuma.data.dao.bean.IKeyed;
import cn.ekuma.data.dao.bean.I_AutoGeneratorStringKey;

import com.openbravo.data.basic.BasicException;
import com.openbravo.data.loader.DataParams;
import com.openbravo.data.loader.DataWrite;
import com.openbravo.data.loader.I_Session;
import com.openbravo.data.loader.PreparedSentence;
import com.openbravo.data.loader.QBFBuilder;
import com.openbravo.data.loader.SentenceExec;
import com.openbravo.data.loader.SentenceExecTransaction;
import com.openbravo.data.loader.SentenceFind;
import com.openbravo.data.loader.SentenceList;
import com.openbravo.data.loader.SerializerReadInteger;
import com.openbravo.data.loader.SerializerWrite;
import com.openbravo.data.loader.SerializerWriteParams;
import com.openbravo.data.loader.SerializerWriteString;
import com.openbravo.data.loader.StaticSentence;
import com.openbravo.data.loader.TableDefinition;
import com.openbravo.data.loader.query.QBFParameter;
import com.openbravo.data.loader.query.QBFParameters;
import com.openbravo.data.user.I_SaveProvider;
import com.openbravo.data.user.I_ListProvider;
import com.openbravo.data.user.ListProviderCreator;
import com.openbravo.data.user.SaveProvider;
import java.util.List;
import java.util.UUID;

/**
 *
 * @author Administrator
 */
public abstract class BaseDAO<T /*extends IKeyed*/> extends ViewDAO<T> {

    protected TableDefinition table;
    SentenceList listSentence;
    SentenceExec updateSentence;
    SentenceExec deleteSentence;
    SentenceFind findSentence;
    SentenceExec insertSentence;
    SentenceFind existSentence;

    public BaseDAO(I_Session s) {
        super(s);
    }

    public T find(IKeyed id) throws BasicException {
        return (T) getFindSentence().find(id);
    }

    public boolean exist(IKeyed id) throws BasicException {
    	int count=(Integer) getExistSentence().find(id);
        return count>0;
    }

    public List<T> list() throws BasicException {
        return getListSentence().list();
    }

    public int update(T t) throws BasicException {
        befareUpdate(t);
        return getUpdateSentence().exec(t);
    }

    public int delete(IKeyed t) throws BasicException {
        befareDelete(t);
        return getDeleteSentence().exec(t);
    }

    public int insert(T t) throws BasicException {
        befareInsert(t);
        return getInsertSentence().exec(t);
    }

    public int delete(final List<IKeyed> objs) throws BasicException {
        return new SentenceExecTransaction(s) {
            public int execInTransaction(Object params) throws BasicException {
                int i = 0;
                for (IKeyed p : objs) {
                    i = delete(p);
                }
                return i;
            }
        }.exec();
    }

    public int update(final List<T> objs) throws BasicException {
        return new SentenceExecTransaction(s) {

            public int execInTransaction(Object params) throws BasicException {
                int i = 0;
                for (T p : objs) {
                    i = update(p);
                }
                return i;
            }
        }.exec();
    }

    public int insert(final List<T> objs) throws BasicException {
        return new SentenceExecTransaction(s) {

            public int execInTransaction(Object params) throws BasicException {
                int i = 0;
                for (T p : objs) {
                    i = insert(p);
                }
                return i;
            }
        }.exec();
    }
    
    public int insertOrUpdate(T t) throws BasicException {
    	if(t instanceof IKeyed){
    		IKeyed key=(IKeyed)t;
    		if(key.getKey()==null||!exist(key))
    			return insert(t);
    	}
        return update(t);
    }
    
    
    public int insertOrUpdate(final List<T> objs) throws BasicException {
        return new SentenceExecTransaction(s) {

            public int execInTransaction(Object params) throws BasicException {
                int i = 0;
                for (T p : objs) {
                    i = insertOrUpdate(p);
                }
                return i;
            }  
        }.exec();
    }
    
    
    public int clear()throws BasicException {
    	return this.getTableDefinition().getClearSentence().exec();
    }

    public void befareInsert(T t) throws BasicException {
        if (t instanceof I_AutoGeneratorStringKey) {
            I_AutoGeneratorStringKey i_GeneratorKey = (I_AutoGeneratorStringKey) t;
            if (i_GeneratorKey.getKey() == null) {
                i_GeneratorKey.setKey(UUID.randomUUID().toString());
            }
        }
    }

    public void befareUpdate(T t) throws BasicException {
    }

    public void befareDelete(IKeyed key) throws BasicException {
    }

    public TableDefinition getTableDefinition() {
        if (table == null) {
            table = getTable();
            table.setFilter(filter);
        }
        return table;
    }

    
    public abstract TableDefinition getTable();

    public SerializerWrite<IKeyed> getKeySerializerWrite() {
        return getKeySerializerWrite(0);
    }
    
    public SerializerWrite<IKeyed> getKeySerializerWrite(final int baseIndex) {
        return new SerializerWrite<IKeyed>() {
            public void writeValues(DataWrite dp, IKeyed obj) throws BasicException {
                Object value = obj.getKey();
                if (value instanceof String) {
                    dp.setString(baseIndex+1, (String) value);
                } else if (value instanceof Integer) {
                    dp.setInt(baseIndex+1, (Integer) value);
                } else if (value instanceof CompositeKey) {
                    CompositeKey compositeKey = (CompositeKey) obj.getKey();
                    for (int i = 1; i < compositeKey.size()+1; i++) {
                        value = compositeKey.getKey(i).getKey();
                        if (value instanceof String) {
                            dp.setString(baseIndex+i, (String) value);
                        } else if (value instanceof Integer) {
                            dp.setInt(baseIndex+i, (Integer) value);
                        }
                    }
                }
            }
        };
    }

    public SerializerWrite<T> getInsertSerializerWrite() {
        return new SerializerWrite<T>() {
            public void writeValues(DataWrite dp, T obj) throws BasicException {
                writeInsertValues(dp, obj);
            }
        };
    }

    public SerializerWrite<T> getUpdateSerializerWrite() {
        return new SerializerWrite<T>() {
            public void writeValues(DataWrite dp, T obj) throws BasicException {
                writeUpdateValues(dp, obj);
            }
        };
    }

    public abstract void writeInsertValues(DataWrite dp, T obj) throws BasicException;
    
    
    public void writeUpdateRealValues(DataWrite dp, T obj) throws BasicException{
    	writeInsertValues(dp,obj);
    }

    public void writeUpdateValues(DataWrite dp, T obj) throws BasicException {
    	writeUpdateRealValues(dp, obj);
        if (obj instanceof IKeyed) {
            Object value = ((IKeyed) obj).getKey();
            int colCount = getTableDefinition().getUpdateColumnCount()+1;
            if (value instanceof String) {
                dp.setString(colCount, (String) value);
            } else if (value instanceof Integer) {
                dp.setInt(colCount, (Integer) value);
            } else if (value instanceof CompositeKey) {
                CompositeKey compositeKey = (CompositeKey) ((IKeyed) obj).getKey();
                for (int i = 0; i < compositeKey.size(); i++) {
                    value = compositeKey.getKey(i).getKey();
                    if (value instanceof String) {
                        dp.setString(colCount + i, (String) value);
                    } else if (value instanceof Integer) {
                        dp.setInt(colCount + i, (Integer) value);
                    }
                }
            }
        }
    }

    public I_ListProvider getQuery() {
        return new ListProviderCreator(getListSentence());
    }

    public SentenceExec getDeleteSentence() {
        if (deleteSentence == null) {
            deleteSentence = getTableDefinition().getDeleteSentence(getKeySerializerWrite());
        }
        return deleteSentence;
    }

    public SentenceFind getFindSentence() {
        if (findSentence == null) {
            findSentence = getTableDefinition().getFindSentence(this, getKeySerializerWrite());
        }
        return findSentence;
    }
    
    public SentenceFind getFindByFieldIndexSentence(int index) {
        return getTableDefinition().getFindByFieldIndexSentence(index);
    }

    public SentenceExec getInsertSentence() {
        if (insertSentence == null) {
            insertSentence = getTableDefinition().getInsertSentence(getInsertSerializerWrite());
        }
        return insertSentence;
    }

    public SentenceList getListSentence() {
        if (listSentence == null) {
            listSentence = getTableDefinition().getListSentence(this);
        }
        return listSentence;
    }

    public SentenceExec getUpdateSentence() {
        if (updateSentence == null) {
            updateSentence = getTableDefinition().getUpdateSentence(getUpdateSerializerWrite());
        }
        return updateSentence;
    }

    public SentenceFind getExistSentence() {
        if (existSentence == null) {
            existSentence = getTableDefinition().getExistSentence(getKeySerializerWrite());
        }
        return existSentence;
    }
    
    public SentenceFind getExistSentence(String[] fields) {
        return getTableDefinition().getExistSentence(fields);
    }

    public I_SaveProvider getSaveProvider() {
        return new SaveProvider(getUpdateSentence(), getInsertSentence(), getDeleteSentence());
    }

    public boolean canUpdate() {
        return getUpdateSentence() != null;
    }

    public boolean canInsert() {
        return getInsertSentence() != null;
    }

    public boolean canDelete() {
        return getDeleteSentence() != null;
    }

    public List list(I_ParentNode parent) throws BasicException {
        String parentFieldName=getTableDefinition().getParentFieldName(transClass(parent.getParentClass()));
        if(parentFieldName!=null&&(parent.getParentKey().getKey() instanceof String))
           return  (List) new PreparedSentence(s, getTableDefinition().getListSQL()+" WHERE "+parentFieldName+"= ?", SerializerWriteString.INSTANCE, this).list(parent.getParentKey().getKey());
        return null;
    }

    public Class transClass(Class in){
        return in;
    }

	@Override
	public List<T> query(QBFParameters filter) throws BasicException {
		return new StaticSentence(s, new QBFBuilder(this.getTable().getSelectQBFSQL()),null,this).list(filter);
	}

	@Override
	public List<T> query(QBFParameter filter) throws BasicException {
		return new StaticSentence(s, new QBFBuilder(this.getTable().getSelectQBFSQL()),null,this).list(filter);
	}
    
	public int updateByField(final IKeyed key,String field,final Object newValue) throws BasicException {
		final int ind=this.getTableDefinition().getFieldIndex(field);
		if(ind==-1)
			throw new BasicException("no find this field:"+field);
		return getTableDefinition().getUpdateSentence(new int[]{ind},SerializerWriteParams.INSTANCE).exec(new DataParams(){
            public void writeValues() throws BasicException {
            	getTableDefinition().getDatas(ind).setValue(dw, 1, newValue);
            	getKeySerializerWrite(1).writeValues(dw, key);
            }
            });
	}
	
	public int updateByFields(final IKeyed key,String[] fields,final Object[] newValue) throws BasicException {
		if(fields.length!=newValue.length)
			throw new BasicException("arg error");
		
		final int[] ind=this.getTableDefinition().getFieldsIndex(fields);
		
		return getTableDefinition().getUpdateSentence(ind,SerializerWriteParams.INSTANCE).exec(new DataParams(){
            public void writeValues() throws BasicException {
            	for(int i=0;i<ind.length;i++){
            	  getTableDefinition().getDatas(ind[i]).setValue(dw, i+1, newValue[i]);
            	}
            	getKeySerializerWrite(ind.length).writeValues(dw, key);
            }
            });
	}
	
	

	public int delete(QBFParameters filter)  throws BasicException{
		 return new StaticSentence(s, new QBFBuilder(getTableDefinition().getDeleteQBFSQL()),null,this).exec(filter);
	}

	public boolean isExistByField(String field, Object value) throws BasicException{
		int count=(Integer) getExistSentence(new String[]{field}).find(new Object[]{value});      
        return count>0;
	}
	
	public boolean isExistByFields(String[] fields, Object[] values) throws BasicException{
		if(fields.length!=values.length)
			throw new BasicException("arg error");
		int count=(Integer) getExistSentence(fields).find(values);      
        return count>0;
	}
	  
    /**
    public List<T> list(List<IKeyed> keys)throws BasicException{
        String listSQL=getTableDefinition().getListSQL()+
        for(IKeyed key:keys){

        }
    }
     * @throws BasicException 
     * */
	
	public int count(QBFParameters filter) throws BasicException{
		return (Integer) new StaticSentence(s, new QBFBuilder(getTableDefinition().getDeleteQBFSQL()),null,SerializerReadInteger.INSTANCE).find(filter);
	}
	
	public Object findObjectPropVal(String propName,String id)throws BasicException{
		int index=getTableDefinition().getFieldIndex(propName);
		return getFindByFieldIndexSentence(index).find(new Object[]{id});
	}
	
}
